//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Workflow.ComponentModel.Design;
using System.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using Microsoft.Research.ScientificWorkflow;
using System.Workflow.Activities;

namespace Microsoft.Research.ScientificWorkflow.Activities
{
    [Name("For Each Loop Activitiy for looping through a list", "For-Each Loop")]
    [Description("Iterates over child activities for each item in the member list.")]
    [WorkflowCategory("Control Flow")]
    [HideActivity(true)]
    public class ForEach : SequenceActivity, 
        IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
    {
        public static DependencyProperty ItemsProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("Items", 
            typeof(IList), typeof(ForEach));

        [RequiredInputParam]
        [Name("Iteration List")]
        [Description("The collection of items to iterate over")]
        public IList Items
        {
            get
            {
                return ((IList)(base.GetValue(ForEach.ItemsProperty)));
            }
            set
            {
                base.SetValue(ForEach.ItemsProperty, value);
            }
        }

        public static DependencyProperty CurrentIterationProperty = 
            System.Workflow.ComponentModel.DependencyProperty.Register("CurrentIteration", 
            typeof(int), typeof(ForEach));

        [OutputParam]
        [Name("Current Iteration")]
        [Description("The current index into the collection of data items")]
        public int CurrentIteration
        {
            get
            {
                return ((int)(base.GetValue(ForEach.CurrentIterationProperty)));
            }
            set
            {
                base.SetValue(ForEach.CurrentIterationProperty, value);
            }
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            CurrentIteration = 0;

            ExecuteIteration(executionContext);

            return ActivityExecutionStatus.Executing;
        }

        private void ExecuteIteration(ActivityExecutionContext parentCtx)
        {
            ForEachBranch branch = this.Activities[0] as ForEachBranch;

            ActivityExecutionContext newContext = parentCtx.ExecutionContextManager.CreateExecutionContext(branch);
            newContext.Activity.RegisterForStatusChange(System.Workflow.ComponentModel.Activity.ClosedEvent, this);
            ((ForEachBranch)newContext.Activity).DataItem = Items[CurrentIteration];

            newContext.ExecuteActivity(newContext.Activity);
        }

        #region IActivityEventListener<ActivityExecutionStatusChangedEventArgs> Members

        public void OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
        {
            //get the AEC for the ForEach activity
            ActivityExecutionContext ctx = sender as ActivityExecutionContext;
            if (ctx == null)
                throw new ArgumentNullException("sender", "The sender must be an AEC");

            //unregister for the events and close the context
            e.Activity.UnregisterForStatusChange(System.Workflow.ComponentModel.Activity.ClosedEvent, this);

            //get the child context and mark it as complete
            ActivityExecutionContext childContext = ctx.ExecutionContextManager.GetExecutionContext(e.Activity);
            ctx.ExecutionContextManager.CompleteExecutionContext(childContext);


            //determine if we should close or execute another iteration
            if (++CurrentIteration >= Items.Count)
                ctx.CloseActivity();
            else
            {
                ExecuteIteration(ctx);
            }
        }

        #endregion
    }
}
